package com.ai.vita;

import com.ai.vita.template.TemplateLoader;
import com.ai.vita.utils.UserAgent;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.VelocityContext;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.SpringContextHolder;
import org.springframework.ui.Model;
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.*;

/**
 * 控制器抽象类:
 * 继承本类的控制器，请实现以下方法：
 * 1、在构造函数里，设置“视图容器路径”widgetContainer，默认采用vita框架里的容器，用来统一处理组件
 * 2、在构造函数里，设置“服务路径”servicePath，不设置会抛出异常，一般值设置为控制层的RequestMapping（不带方法的RequestMapping），给组件提供统一的服务调用
 * 3、可以实现一个方法，来调用achieveData方法，也可以直接调用，用来组件刷新模版时所需数据的获取
 * 4、必须实现一个方法，来调用callService方法，RequestMapping值必须为callService，用来给组件提供统一的服务调用方法
 * 5、必须实现一个方法，来调用refreshView方法，RequestMapping值必须为refreshView，用来给组件提供统一的模版刷新
 * 6、必须实现一个方法，来调用refreshPart方法，RequestMapping值必须为refreshPart，用来给组件提供统一的模版局部刷新或者增量
 * 7、可以实现一个方法，来重载refreshSession方法，RequestMapping值必须为refreshSession，用来刷新session，js里提供该功能，因为session处理存在不一致性，故不统一实现
 * 8、可以实现一个方法，来重载removeSession方法，RequestMapping值必须为removeSession，用来清除session，js里提供该功能，因为session处理存在不一致性，故不统一实现
 * 9、可以实现一个方法，来重载removeParty方法，RequestMapping值必须为removeParty，用来清除session里的party信息，js里提供该功能，因为session处理存在不一致性，故不统一实现
 * 10、可以实现一个方法，来重载refreshParty方法，RequestMapping值必须为refreshParty，用来刷新session里的party信息，js里提供该功能，因为session处理存在不一致性，故不统一实现
 */
public abstract class AbstractController extends AbstractComponent {

    protected static final Logger logger = LoggerFactory.getLogger(AbstractController.class);

    /**
     * 视图容器路径
     */
    private String widgetContainer = "vita/container/widgetContainer";

    public String getWidgetContainer() {
        return widgetContainer;
    }

    public void setWidgetContainer(String widgetContainer) {
        this.widgetContainer = widgetContainer;
    }

    /**
     * 协助容器路径
     */
    private String assistContainer = "vita/container/assistContainer";

    public String getAssistContainer() {
        return assistContainer;
    }

    public void setAssistContainer(String assistContainer) {
        this.assistContainer = assistContainer;
    }

    /**
     * 服务路径
     */
    private String servicePath = "";

    public String getServicePath() {
        return servicePath;
    }

    public void setServicePath(String servicePath) {
        this.servicePath = servicePath;
    }

    /**
     * 模版分类
     */
    private Map<String, String> classifications = null;

    public Map<String, String> getClassifications() {
        return classifications;
    }

    public void setClassifications(Map<String, String> classifications) {
        this.classifications = classifications;
    }

    /**
     * 获取（模版）分类
     * @param request
     * @return
     */
    protected String getClassification(HttpServletRequest request){
        String classification = null;
        if(getClassifications() != null){
            UserAgent ua = UserAgent.parse(request);
            classification = classifications.get(ua.getDeviceType());
        }
        return classification;
    }

    /**
     * 刷新视图
     * @param widgetName
     * @param args
     * @param m
     * @return
     * @throws Exception
     */
    protected String refreshView(String widgetName, String classification, Object[] args, Model m) throws Exception {
        String resourcesPath = TemplateLoader.getTemplatePath(widgetName + "-tpl", "html", classification);
        if(resourcesPath == null){
            throw new Exception("视图刷新时，未找到视图模版，模版名称为" + widgetName + "-tpl.html");
        }
        //获取第三个包名之后的路径，CRM里前三个包路径由velocity配置
        int startIndex = StringUtils.ordinalIndexOf(resourcesPath, "/", 3) + 1, endIndex = resourcesPath.indexOf(".");
        resourcesPath = resourcesPath.substring(startIndex, endIndex);
        Map options = achieveData(widgetName, args);
        if(options != null){
            m.addAttribute("options", JSONObject.fromObject(options));
        }
        return resourcesPath;
    }

    /**
     * 刷新视图
     * @param request
     * @param m
     * @return
     * @throws Exception
     */
    public String refreshView(HttpServletRequest request, Model m) throws Exception {
        String wName = request.getParameter("widgetName");
        String params = request.getParameter("params");
        Object[] args = jsonConverter.toBean(params, Object[].class);
        String classification = getClassification(request);
        return refreshView(wName, classification, args, m);
    }

    /**
     * 刷新局部视图
     * @param widgetName
     * @param methodName
     * @param args
     * @param selector
     * @param keyName
     * @param writer
     */
    protected void refreshPart(String widgetName,String classification, String methodName, Object[] args, String selector, String keyName, Writer writer) throws Exception {
        //数据处理
        Object data = invokeMethod(widgetName, methodName, args);
        VelocityContext ctx = new VelocityContext();
        Map options = new HashMap();
        if(data instanceof Map){
            //如果返回值是map类型
            Iterator<String> keySet = ((Map) data).keySet().iterator();
            for (;keySet.hasNext();) {
                String key = keySet.next();
                options.put(key, ((Map) data).get(key));
            }
        }else if(keyName != null){
            if(data instanceof List){
                options.put(keyName, JSONArray.fromObject(data));
            }else{
                options.put(keyName, JSONObject.fromObject(data));
            }
        }else{
            throw new RuntimeException("获取到的数据不是map类型，或者没有指定keyName");
        }
        ctx.put("options", JSONObject.fromObject(options));
        //模版处理
        String resources = TemplateLoader.getTemplate(widgetName + "-tpl", "html", classification);
        Document doc = Jsoup.parse(resources);
        Element e = doc.select(selector).first();
        //模版引擎
        VelocityConfigurer vc = SpringContextHolder.getBean("velocityConfigurer");
        vc.getVelocityEngine().evaluate(ctx, writer, "", StringEscapeUtils.unescapeHtml(e.toString()));
    }

    /**
     * 刷新局部视图
     * @param request
     * @param response
     * @throws Exception
     */
    public void refreshPart(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setContentType("text/html;charset=utf-8");
        String widgetName = request.getParameter("widgetName");
        String methodName = request.getParameter("methodName");
        String params = request.getParameter("params");
        Object[] args = jsonConverter.toBean(params, Object[].class);
        String selector = request.getParameter("selector");
        String keyName = request.getParameter("keyName");
        String classification = getClassification(request);
        refreshPart(widgetName, classification, methodName, args, selector, keyName, response.getWriter());
    }

    /**
     * 组件所需服务统一调用
     * @param request
     * @return
     * @throws IOException
     */
    protected Object callService(HttpServletRequest request) throws Exception {
        String widgetName = request.getParameter("widgetName");
        String methodName = request.getParameter("methodName");
        String params = request.getParameter("params");
        Object[] args = jsonConverter.toBean(params, Object[].class);
        return invokeMethod(widgetName, methodName, args);
    }

    /**
     * 初始化视图
     * @param compCode
     * @param title
     * @param options
     * @param m
     * @return
     */
    protected String initView(String compCode, String title, Object options, Model m, HttpServletRequest request) throws Exception {
        String inFrame = request.getParameter("inFrame");
        String classification = getClassification(request);
        m.addAttribute("classification", classification);
        m.addAttribute("inFrame", "true".equals(inFrame) ? true : false);
        m.addAttribute("compCode", compCode);
        m.addAttribute("title", title);
        m.addAttribute("version", getVersion());
        if(options == null){
            options = new HashMap();
        }
        JSONObject o = JSONObject.fromObject(options);
        //o.put("classification", classification);
        m.addAttribute("compOptions", o);
        //必须设定服务路径
        if("".equals(getServicePath())){
            throw new Exception("请在你的controller的构造函数里，或者在调用本方法之前，给this.servicePath指定基础容器的地址");
        }else{
            m.addAttribute("servicePath", getServicePath());
        }
        //必须设定组件容器路径
        if("".equals(getWidgetContainer())){
            throw new Exception("请在你的controller的构造函数里，或者在调用本方法之前，给this.widgetContainer指定基础容器的地址");
        }
        return widgetContainer;
    }

    /**
     * 协助能力初始化
     * @param request
     * @param m
     * @return
     */
    protected String initAssist(HttpServletRequest request, Model m) throws Exception {
        String assistName = request.getParameter("assist");
        String assist = TemplateLoader.getTemplate(assistName + "-assist", "js");
        String inFrame = request.getParameter("inFrame");
        String appoint = request.getParameter("appoint");
        //处理指定的节点
        if(appoint != null){
            StringBuffer ta = new StringBuffer();
            int sp = assist.indexOf("{") + 1;
            ta
                    .append(assist.substring(0, sp))
                    .append("appointedNode : \"").append(appoint).append("\",")
                    .append(assist.substring(sp));
            assist = ta.toString();
        }
        m.addAttribute("inFrame", "true".equals(inFrame) ? true : false);
        m.addAttribute("assist", assist);
        m.addAttribute("assistName", assistName);
        m.addAttribute("version", getVersion());
        //传递流程的参数，除了assist和inFrame
        Enumeration e = request.getParameterNames();
        Map map = new HashMap();
        for(;e.hasMoreElements();){
            String paramName = e.nextElement().toString();
            if(!paramName.equals("assist") && !paramName.equals("inFrame") && !paramName.equals("appoint")){
                String paramValue = request.getParameter(paramName);
                map.put(paramName, paramValue);
            }
        }
        if(!map.isEmpty()){
            m.addAttribute("params", jsonConverter.toJson(map));
        }
        //必须设定服务路径
        if("".equals(getServicePath())){
            throw new Exception("请在你的controller的构造函数里，或者在调用本方法之前，给this.servicePath指定基础容器的地址");
        }else{
            m.addAttribute("servicePath", getServicePath());
        }
        //必须设置协助能力容器的路径
        if("".equals(getAssistContainer())){
            throw new Exception("请在你的controller的构造函数里，或者在调用本方法之前，给this.assistContainer指定协助能力的容器的地址");
        }
        return getAssistContainer();
    }

    /**
     * 刷新session
     * @param session
     */
    protected void refreshSession(String session){}

    /**
     * 清空session
     */
    protected void removeSession(){}

    /**
     * 清空session里的客户信息
     */
    protected void removeParty(){}

    /**
     * 刷新session里的客户信息
     */
    protected void refreshParty(){}

    /**
     * 该方法在该类中暂时无用，为了实现而实现，避免子类一定要实现接口方法
     * @param params
     * @return
     * @throws Exception
     */
    public Map achieveData(Object... params) throws Exception {
        return null;
    }
}